package com.interactive1.utils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
/**
 * @version v2.06
 */
public class Functions {
	/**
	 * @since v2.0
	 */
	public static final String _VERSION = "v2.06";
	public static int fastStrToInt(String str) {
		int index = str.length()-1;
		int result = 0;
		result = (int)str.charAt(index--)-48;
		if (index<0) return result;
		result += ((int)str.charAt(index--)-48)*10;
		if (index<0) return result;
		result += ((int)str.charAt(index--)-48)*100;
		if (index<0) return result;
		result += ((int)str.charAt(index--)-48)*1000;
		if (index<0) return result;
		result += ((int)str.charAt(index--)-48)*10000;
		if (index<0) return result;
		result += ((int)str.charAt(index--)-48)*100000;
		if (index<0) return result;
		result += ((int)str.charAt(index--)-48)*1000000;
		if (index<0) return result;
		result += ((int)str.charAt(index--)-48)*10000000;
		if (index<0) return result;
		result += ((int)str.charAt(index--)-48)*100000000;
		if (index<0) return result;
		result += ((int)str.charAt(index--)-48)*1000000000;
		if (index<0) return result;
		return -1;
	}


	public static String fastUpperCase(String str) {
		char[] letters = new char[str.length()];
		for (int i=0;i<letters.length;i++)
			if ((int)str.charAt(i)>96 && (int)str.charAt(i)<123)
				letters[i]=(char)(((int)str.charAt(i))-32);
			else
				letters[i]=str.charAt(i);
		return new String(letters);
	}
	public static String fastLowerCase(String str) {
		char[] letters = new char[str.length()];
		for (int i=0;i<letters.length;i++)
			if ((int)str.charAt(i)>64 && (int)str.charAt(i)<91)
				letters[i]=(char)(((int)str.charAt(i))+32);
			else
				letters[i]=str.charAt(i);
		return new String(letters);
	}

	/**
	 *	replaces single quote with two single qouotes (for use in sql)
	 */
	public static String fixQuote(String quotes)
	{
		if (quotes==null) return null;
		StringBuffer s = new StringBuffer();
		java.util.StringTokenizer st = new java.util.StringTokenizer(quotes,"'�\\",true);

		while (st.hasMoreTokens())
		{
			String t = st.nextToken();
			if ("�".equals(t)) t = "'";
			if ("'".equals(t)) s.append(t);
			if ("\\".equals(t)) s.append(t);
			s.append(t);
		}
		return s.toString();
	}

	/**
	 * replaces single quote with two single qouotes
	 * @since v2.05
	 */
	public static String duplicateQuotes(String str)
	{
		if (str==null) return null;
		StringBuffer s = new StringBuffer();
		java.util.StringTokenizer st = new java.util.StringTokenizer(str, "\"", true);

		while (st.hasMoreTokens())
		{
			String t = st.nextToken();
			if ("\"".equals(t)) s.append(t);
			s.append(t);
		}
		return s.toString();
	}

	/**
	 *	changes single quote to &quot (for displaying varibles in HTML)
	 *	and changes " < > to the HTML entities format
	 *   except the &!!!
	 */
	public static String stripQuote(String quotes)
	{
		if (quotes==null) return null;
		StringBuffer s = new StringBuffer();
		java.util.StringTokenizer st = new java.util.StringTokenizer(quotes,"\"<>'",true);
		while (st.hasMoreTokens())
		{
			String token = st.nextToken();
			if ("'".equals(token))
				s.append("&#39;");
			else if ("\"".equals(token))
				s.append("&quot;");
			else if ("<".equals(token))
				s.append("&lt;");
			else if (">".equals(token))
				s.append("&gt;");
			else
				s.append(token);
		}
		return s.toString();
	}


	/**
	 *	returns true if the string is null,empty or if it contains whitespaces only
	 */
	public static boolean Empty(String str) {
		return (str!=null && str.trim().length()>0);
	}

	/**
	 *	replaces HTML tag enclosing characters to &gt and &lt
	 * does not strip bold,italic and underline tags
	 */
	public static String stripHTML(String src) {
		if (src==null) return null;
		StringBuffer ret = new StringBuffer();
		StringBuffer buffer = new StringBuffer();
		StringBuffer temp=null;
		java.util.StringTokenizer st = new java.util.StringTokenizer(src,"<>\"'",true);
		String token;
		boolean buff=false;
		while (st.hasMoreTokens())
		{
			token=st.nextToken();

			if ("\"".equals(token)) token="&quot;";
			if ("'".equals(token)) token="&#39;";

			if ("<".equals(token) && buffer.length()==0) {
				temp=ret; 	//save result
				ret=buffer;	//use buffer from now on
				buff=true;
			}

			if (">".equals(token)) {
				if (ret.length()>1) {
					String tag=ret.toString().toUpperCase();

					if (!allowedtag(tag,ALLOWEDTAGS))
					{
						int len = tag.length();
						for (int j=0;j<len;j++)
							if (ret.charAt(j)=='<')
							{
								ret.setCharAt(j,'&');
								ret.insert(j+1,"lt;");
								len+=3;
							}
					}
					if (buff) {
						buffer=ret;						//save what we got to buffer
						ret=temp;						//restore result
						ret.append(buffer.toString());	//append buffer to result
						buffer.setLength(0);			//clear buffer
						buff=false;
					}
				}
			}
			ret.append(token);
		}

		if (buff) {
			buffer=ret;
			ret=temp;
			ret.append(buffer.toString());
		}

		return ret.toString();
	}
	/**
	 *	replaces HTML tag enclosing characters to &gt and &lt
	 * except given tags - example (strips all html tags except bold,italic and underline):<BR>
	 *	String[] allowedTags = new String[] {"B","I","U"};<BR>
	 * out.println(stripHTML(someText,allowedTags);
	 */
	public static String stripHTML(String src,String[] allowedTags) {
		if (src==null) return null;
		StringBuffer ret = new StringBuffer();
		StringBuffer buffer = new StringBuffer();
		StringBuffer temp=null;
		java.util.StringTokenizer st = new java.util.StringTokenizer(src,"<>\"",true);
		String token;
		boolean buff=false;
		while (st.hasMoreTokens())
		{
			token=st.nextToken();

			if ("\"".equals(token)) token="&quot;";

			if ("<".equals(token) && buffer.length()==0) {
				temp=ret; 	//save result
				ret=buffer;	//use buffer from now on
				buff=true;
			}

			if (">".equals(token)) {
				if (ret.length()>1) {
					String tag=ret.toString().toUpperCase();

					if (!allowedtag(tag,allowedTags))
					{
						int len = tag.length();
						for (int j=0;j<len;j++)
							if (ret.charAt(j)=='<')
							{
								ret.setCharAt(j,'&');
								ret.insert(j+1,"lt;");
								len+=3;
							}
					}
					if (buff) {
						buffer=ret;						//save what we got to buffer
						ret=temp;						//restore result
						ret.append(buffer.toString());	//append buffer to result
						buffer.setLength(0);			//clear buffer
						buff=false;
					}
				}
			}
			ret.append(token);
		}

		if (buff) {
			buffer=ret;
			ret=temp;
			ret.append(buffer.toString());
		}

		return ret.toString();
	}


	/*
	 * prepares string for use as javascript string constant 
	 * by escaping single and double quotes if not allready escaped 
	 */
	public static String escapeJS(String what) {
		int l = what.length();
		StringBuffer sb = new StringBuffer();
		char c;
		for (int i=0;i<l;i++) {
			c = what.charAt(i);
			if (c=='\'' || c=='"')
				if (i==0 || what.charAt(i-1)!='\\') 
					sb.append("\\");
			sb.append(c);
		}
		return sb.toString();
	}

	public static String JS_escapeQuot(String str) {
		if (str==null) return null;
		StringBuffer s = null;
		java.util.StringTokenizer st = null;
		
		// check 
		if (str.indexOf('\"') > -1 || str.indexOf('\'') > -1 || str.indexOf('\n') > -1 || str.indexOf('\r') > -1) {
			s = new StringBuffer();
			st = new java.util.StringTokenizer(str,"\"'\n\r",true);
			while (st.hasMoreTokens())
			{
				String token = st.nextToken();
				if ("'".equals(token))
					s.append("\\'");
				else if ("\"".equals(token))
					s.append("&quot;");
				else if ("\n".equals(token))
					s.append("\\n");
				else if ("\r".equals(token))
					continue;
				else
					s.append(token);
			}
			str = s.toString();
		}
		return str;
	}


	public static String cleanHTML(String src) {
		if (src==null) return null;
		StringBuffer ret = new StringBuffer();
		StringBuffer buffer = new StringBuffer();
		StringBuffer temp=null;
		java.util.StringTokenizer st = new java.util.StringTokenizer(src,"<>\"",true);
		String token;
		boolean buff=false;
		while (st.hasMoreTokens())
		{
			token=st.nextToken();

			if ("\"".equals(token)) token="&quot;";

			if ("<".equals(token) && buffer.length()==0) {
				temp=ret; 	//save result
				ret=buffer;	//use buffer from now on
				buff=true;
			}

			if (">".equals(token)) {
				token="";
				if (ret.length()>1) {
					if (buff) {
						buffer=ret;						//save what we got to buffer
						ret=temp;						//restore result
						buffer.setLength(0);			//clear buffer
						buff=false;
					}
				}
			}
			ret.append(token);
		}

		if (buff) {
			buffer=ret;
			ret=temp;
//			ret.append(buffer.toString());
		}

		return ret.toString();
	}


	private static final String[] ALLOWEDTAGS = { "B", "I", "U" };
	private static boolean allowedtag(String tag, String[] allowedTags) {
		for (int i=0;i<allowedTags.length;i++) {
			if (tag.endsWith("<"+allowedTags[i]) || tag.endsWith("</"+allowedTags[i])) return true;
		}
		return false;
	}


	public static String CRtoBR(String text) {
		java.util.StringTokenizer st = new java.util.StringTokenizer(text,"\n");
		StringBuffer ret = new StringBuffer();
		if (st.hasMoreTokens()) {
			ret.append(st.nextToken());
			while (st.hasMoreTokens()) {
				ret.append("<BR>");
				ret.append(st.nextToken());
			}
		}
		return ret.toString();
	}


	public static void copyFile(String srcfile,String destfile) throws Exception {
		copyFile(new File(srcfile),new File(destfile));
	}

	public static void copyFile(File srcfile,File destfile) throws Exception {
		boolean error = false;
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			byte [] buffer = new byte [32768];
			int n;
			
			fis = new FileInputStream(srcfile);
			fos = new FileOutputStream(destfile);
	
			while ((n = fis.read(buffer)) > -1) {
				fos.write(buffer, 0, n);
			}
		} catch (Exception e) {
			error = true;
		} finally {
			if (fis!=null) fis.close();
			if (fos!=null) fos.close();
			if (error) {
				destfile.delete();
			}
		}
	}

	public static void copyDir(String srcfile,String destfile) throws Exception {
		File s = new File(srcfile);
		if (!s.isDirectory()) return;
		File d = new File(destfile);
		d.mkdirs();
		String[] fns = s.list();
		if (fns==null) return;
		for (int i=0;i<fns.length;i++) {
			File f = new File(s,fns[i]);
			if (f.isDirectory()) copyDir(srcfile+"/"+fns[i],destfile+"/"+fns[i]);
			else copyFile(srcfile+"/"+fns[i],destfile+"/"+fns[i]);
		}
	}

	public static void rmDir(String dir) throws Exception {
		File d = new File(dir);
		String[] fns = d.list();
		if (fns==null) return;
		for (int i=0;i<fns.length;i++) {
			File f = new File(d,fns[i]);
			if (f.isDirectory()) rmDir(dir+"/"+fns[i]);
			else f.delete();
		}
		d.delete();
	}

	/**
	 *	removes '\n' charatchters from string
	 */
	public static String cleanLineBreakes(String src) {
		if (src==null) return null;
		StringBuffer buffer = new StringBuffer();
		java.util.StringTokenizer st = new java.util.StringTokenizer(src,"\n\r",true);
		String token;

		while (st.hasMoreTokens()){
			token=st.nextToken();
			if ("\n".equals(token)) token="";
			if ("\r".equals(token)) token="";
			buffer.append(token);
		}
		return buffer.toString();
	}


	/**
	 * Returns either the given value, or def if value was null.
	 * @param value given value
	 * @param def default value
	 * @return value, or def if value was null
	 * @since v2.01
	 */
	public static String defaultValue(String value, String def) {
		return value != null ? value : def;
	}
	/**
	 * Fill a text with a leading padding character, up to a total defined size.
	 * If a text is longer than the 'width' parameter, no padding will be done.
	 * If a text is null, it will be considered the same as if it was 'null'.
	 *
	 * @param text text to be formated
	 * @param fill padding character
	 * @param width minimum size of the final string
	 * @return the formatted string
	 * @author Arsen Torbarina, Interactive1
	 * @since v2.0
	 */
	public static String padded(String text, char fill, int width) {
		return paddedAlignRight(text, fill, width);
	}

	/**
	 * Format a number with leading zeros, up to a total defined size.
	 * If the starting size is longer than the 'width' parameter, no padding will be done.
	 *
	 * @param number number to be formatted
	 * @param width minimum final size
	 * @return the formatted number shown as a string with the leading zeros
	 * @author Arsen Torbarina, Interactive1
	 * @since v2.0
	 */
	public static String padded(long number, int width) {
		return paddedAlignRight(String.valueOf(number), '0', width);
	}

	/**
	 * Format a number with a leading character, up to a total defined size.
	 * If the starting size is longer than the 'width' parameter, no padding will be done.
	 *
	 * @param number number to be formatted
	 * @param fill padding character
	 * @param width minimum final size
	 * @return the formatted number shown as a string with the leading zeros
	 * @author Arsen Torbarina, Interactive1
	 * @since v2.0
	 */
	public static String padded(long number, char fill, int width) {
		return paddedAlignRight(String.valueOf(number), fill, width);
	}
	/**
	 * Fill a text with a trailing padding character, up to a total defined size.
	 * If a text is longer than the 'width' parameter, no padding will be done.
	 * If a text is null, it will be considered the same as if it was 'null'.
	 *
	 * @param text text to be formated
	 * @param fill padding character
	 * @param width minimum size of the final string
	 * @return the formatted string
	 * @author Arsen Torbarina, Interactive1
	 * @since v2.0
	 */
	public static String paddedAlignLeft(String text, char fill, int width) {
		return text + paddingStr(text, fill, width);
	}
	/**
	 * The same as <code>padded(String text, char fill, int width)</code>.
	 *
	 * @see #padded(String text, char fill, int width)
	 * @author Arsen Torbarina, Interactive1
	 * @since v2.0
	 */
	public static String paddedAlignRight(String text, char fill, int width) {
		return paddingStr(text, fill, width) + text;
	}
	/**
	 * @author Arsen Torbarina, Interactive1
	 * @since v2.0
	 */
	private static String paddingStr(String text, char fill, int width) {
		text += "";
		int j = width - text.length();
		StringBuffer sb = new StringBuffer("");
		for (int i = 0; i<j; i++) {
			sb.append(fill);
		}
		return sb.toString();
	}
	public static String hashSQL(String str) {
		if (str == null) return null;
		char[] chrs = str.toCharArray();
		for (int i = 0; i<chrs.length; i++) {
			char c = chrs[i];
			if (c == '\'') c++;
			chrs[i] = c;
		}
		return new String(chrs);
	}

	/**
	 * Maps all characters between 0 - 31 and above 127 to the ASCII-safe
	 * range between 32 and 127.
	 * <p>Used only for hashing purposes, that is, the processed string
	 * could look "ugly".
	 *
	 * @param str input string
	 * @return processed string or null when str is null
	 * @since v2.0
	 */
	public static String hashASCII(String str) {
		if (str == null) return null;
		char[] chrs = str.toCharArray();
		for (int i = 0; i<chrs.length; i++) {
			char c = chrs[i];
			if (c>127) c &= 0x7F;
			if (c<32) c += 32;
			chrs[i] = c;
		}
		return new String(chrs);
	}

	/**
	 * Returns an SQL-safe irreversible shadow of the string (that is,
	 * the returned string can be safely used in SQL queries).
	 * <p>Used for shadowing passwords.
	 *
	 * @param str input string, containing e.g. a password
	 * @return hash string or null when str is null
	 * @since v2.0
	 */
	public static String shadow(String str) {
		return (hashSQL(hashASCII(shadowRaw(str))));
	}

	/**
	 * Returns an irreversible shadow of the string. The returned string
	 * can contain unprintable chars, so it is recommended to use
	 * <code>shadow</code> method when the result has to be stored in
	 * a database or printed.
	 *
	 * <p>Used for shadowing passwords.
	 *
	 * @param str input string, containing e.g. a password
	 * @return hash string or null when str is null
	 * @see #shadow
	 * @since v2.0
	 */
	public static String shadowRaw(String str) {
		if (str == null) return null;
		try {
			MessageDigest md = MessageDigest.getInstance("SHA");
			synchronized (md) {
				md.update(str.getBytes());
				return new String(md.digest());
			}
		} catch (NoSuchAlgorithmException e) {
			return null;
		}
	}
	/**
	 * @since v2.02
	 */
	public static String replace(String text, String find, String replace) {
		StringBuffer sb = new StringBuffer(text);
		int pos, newstart=0;
		while ((pos=sb.toString().indexOf(find, newstart))>=0) {
			sb.replace(pos, pos+find.length(), replace);
			newstart = pos+replace.length();
		}
		return sb.toString();
	}
	/**
	 * Removes <script>...</script> and <style>....</style>
	 * @since v2.02
	 */
	public static String removeFromHTML(String text, String start, String end) {
		StringBuffer sb = new StringBuffer(text);
		int pos, pos2, newstart=0;
		while (((pos=sb.toString().indexOf(start, newstart))>=0) && ((pos2=sb.toString().indexOf(end, newstart))>=0)) {
			pos2 = pos2+end.length();
			sb.replace(pos, pos2, "");
			newstart = pos2;
		}
		return sb.toString();
	}
	/**
	 * Removes <script>...</script> and <style>....</style>
	 * @since v2.04
	 */
	public static String noNull(String s) {
		return (s!=null ? s : "");
	}
	/**
	 * Makes file-name from a string
	 * @since v2.05
	 */
	public static String makeFileName(String s) {
		s = s.trim();
		StringBuffer sb = new StringBuffer();
		final String allowed = ".~-";
		for (int i=0; i<s.length(); i++) {
			char ch = s.charAt(i);
			char chl = Character.toLowerCase(ch);
			String chs;
			if (ch==' ') chs = "_";
			if (chl>='a' && chl<='z' || ch>='0' && ch<='9' || allowed.indexOf(ch)>=0)  {
				chs = ""+ch;
			} else  {
				chs = "";
			}
			sb.append(chs);
		}
		return sb.toString();
	}


	/**
	 * Trims word endings and delimiters from the start of the input string to the word boundary
	 * @since v2.06
	 */
	public static String trimStart(String input, String delimiter)
	{
		int min = input.length();
		int index;
		boolean startsWithDelimiter = false;

		for (int i=0; i<delimiter.length(); i++)
			if (input.startsWith(delimiter.substring(i,i+1))) startsWithDelimiter = true;

		if (! startsWithDelimiter) return input;

		for (int i=0; i<delimiter.length(); i++)
		{
			index=input.indexOf(delimiter.substring(i,i+1));
			if (index<min && index>-1) min=index;
		}
		if (min==input.length()) min=0;
		else min++;

		while (input.substring(min).startsWith(" ") || input.substring(min).startsWith(".") || input.substring(min).startsWith(",")
		        || input.substring(min).startsWith(";") || input.substring(min).startsWith(":") || input.substring(min).startsWith("!") || input.substring(min).startsWith("?"))
			min++;

		if (min>=input.length())min=input.length();
		return input.substring(min);
	}

	/**
	 * Trims word beginnings from the end of the input string to the word boundary
	 * @since v2.06
	 */
	public static String trimEnd(String input, String delimiter)
	{
		int max=-1;
		int index;
		for (int i=0; i<delimiter.length(); i++)
		{
			index=input.lastIndexOf(delimiter.substring(i,i+1));
			if (index>max) max=index;
		}
		if (max==-1) max=input.length();
		else max++;
		return input.substring(0,max);
	}
	/** Name of the method say everything */
	public static String csvStrFromStrArr(String[] strArr)
	{
		StringBuffer elist = new StringBuffer();
		if (strArr!=null)
		{
			for (int i=0;i<strArr.length;i++)
				elist.append(strArr[i]+",");
			elist.setLength(elist.length()-1);
		}
		return elist.toString();
	}

	public static Collection csvToCollection(String csv)
	{
		return csvToCollection(csv, ",;");
	}

	/** The name of the method say everything and I am not repeating :-)*/

	public static String csvStrFromCollection(Collection c, String delim)
	{
		StringBuffer elist = new StringBuffer();
		String d = "";
		if (c!=null) {
			for (Iterator iterator = c.iterator(); iterator.hasNext();) {
				String s = (String)iterator.next();
				elist.append(d+s);
				d = delim;
			}
		}
		return elist.toString();
	}

	public static String csvStrFromCollection(Collection c) {
		return csvStrFromCollection(c, ",");
	}
	
	public static Collection csvToCollection(String csv, String delims)
	{
		Collection coll = new TreeSet();
		if (csv==null) return coll;
		StringTokenizer st = new StringTokenizer(csv, delims);
		while (st.hasMoreTokens()) {
			coll.add(st.nextToken());
		}
		return coll;
	}

	/**
	 * Validates email
	 */
	final static String EM_RX =	"^([\\w\\+\\-\\'])+((\\.)([\\w\\+\\-\\'])+)*\\@(([\\w\\-])+\\.)+([a-zA-Z0-9]{2,4})+$"; 

	public static boolean validateEmail(String email)
	{
		
		return java.util.regex.Pattern.matches(EM_RX, email);
	}

	public static String parseDateString(String date){
		String result = null;

		try{
			String day = null;
			String month = null;
			String year = null;
			String hour = null;
			String minute = null;

			StringTokenizer st = new StringTokenizer(date, "/-: .");

			int i = 0;
			while(st.hasMoreTokens()){
				switch(i){
					case 0:
						day = st.nextToken();
						break;
					case 1:
						month = st.nextToken();
						break;
					case 2:
						year = st.nextToken();
						break;
					case 3:
						hour = st.nextToken();
						break;
					case 4:
						minute = st.nextToken();
						break;
				}
				i++;
			}

			// validating date fields
			try{
				Integer.parseInt(day);
				Integer.parseInt(month);
				Integer.parseInt(year);
			} catch(NumberFormatException e1){
				return date;
			}

			if(day.length() == 1) day = "0" + day;
			if(month.length() == 1) month = "0" + month;
			if(year.length() == 2){
				if(Integer.parseInt(year) > 30) year = "19" + year;
				else year = "20" + year;
			}

			if(day.length() == 2 && month.length() == 2 && year.length() == 4) result = day + "." + month + "." + year;
			else return date;

			try{
				Integer.parseInt(hour);
				Integer.parseInt(minute);
			} catch(NumberFormatException e1){
				result += " 00:00";
				return result;
			}

			if(hour.length() == 1) hour = "0" + hour;
			if(minute.length() == 1) minute = "0" + minute;

			if(hour.length() == 2 && minute.length() == 2 && Integer.parseInt(hour) < 25 && Integer.parseInt(minute) < 61)
				result += " " + hour + ":" + minute;
			else result += " 00:00";

		} catch(Exception e){
			System.out.println(e.getMessage());
		}

		return result;
	}

	public static String millisToStr(String millis,String format) {
		return millisToStr(Long.parseLong(millis),format);
	}
	public static String millisToStr(long millis,String format) {
		return dateToStr(new Date(millis),format);
	}
	public static String dateToStr(Date millis,String format) {
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		return sdf.format(millis);
	}
	public static String calendarToStr(Calendar millis,String format) {
		return dateToStr(millis.getTime(),format);
	}

	public static GregorianCalendar parseDate(String date) {
		if (date==null || date.trim().length()==0) throw new IllegalArgumentException();
		GregorianCalendar gc = new GregorianCalendar(0,0,0);
		StringTokenizer st = new StringTokenizer(date," -; .:\\/");
		if (st.hasMoreTokens())
			gc.set(Calendar.YEAR,Integer.parseInt(st.nextToken()));
		if (st.hasMoreTokens())
			gc.set(Calendar.MONTH,Integer.parseInt(st.nextToken())-1);
		if (st.hasMoreTokens())
			gc.set(Calendar.DATE,Integer.parseInt(st.nextToken()));
		if (st.hasMoreTokens())
			gc.set(Calendar.HOUR_OF_DAY,Integer.parseInt(st.nextToken()));
		if (st.hasMoreTokens())
			gc.set(Calendar.MINUTE,Integer.parseInt(st.nextToken()));
		return gc;
	}


	public static void unzip(File zipFile, File destDir) throws ZipException, IOException {
		System.out.println("UNZIP: " + zipFile.getAbsolutePath() + ", to = " + destDir.getAbsolutePath());
		long start = System.currentTimeMillis();
		long mark = start;
		long end = start;
		
		System.out.print("opening zip file");
		ZipFile zip = new ZipFile(zipFile);
		try {
			end = System.currentTimeMillis(); System.out.println("- DONE (" + (end-mark) + "ms)"); mark = end;
			
			File tmpFile = null;
			
			destDir.mkdirs();
			
			// create dirs
			System.out.print("retriving entries");
			Enumeration enums = zip.entries();
			end = System.currentTimeMillis(); System.out.println("- DONE (" + (end-mark) + "ms)"); mark = end;
			while (enums.hasMoreElements()) {
				ZipEntry entry = (ZipEntry)enums.nextElement();
				if (entry.isDirectory()) {
					System.out.print("creating dir: " + entry.getName());
					tmpFile = new File(destDir, entry.getName());
					tmpFile.mkdirs();
					end = System.currentTimeMillis(); System.out.println("- DONE (" + (end-mark) + "ms)"); mark = end;
				}
			}
			
			// extract files
			System.out.print("retriving entries");
			enums = zip.entries();
			end = System.currentTimeMillis(); System.out.println("- DONE (" + (end-mark) + "ms)"); mark = end;
			byte b[] = new byte[500 * 1024];
			while (enums.hasMoreElements()) {
				ZipEntry entry = (ZipEntry)enums.nextElement();
				if (!entry.isDirectory()) {
					//System.out.print("extracting file: " + entry.getName());
					InputStream is = new BufferedInputStream( zip.getInputStream(entry) );
					tmpFile = new File(destDir, entry.getName());
					OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile));
					int n = -1;
					while ((n=is.read(b)) > -1) {
						os.write(b, 0, n);
					}
					is.close();
					os.close();
					//end = System.currentTimeMillis(); System.out.println("- DONE (" + (end-mark) + "ms)"); mark = end;
				}
			}
		} finally {
			zip.close();
		}
		end = System.currentTimeMillis(); System.out.println("UNZIP DONE (" + (end-start) + "ms)");
	}

	public static String trimChars(String str, String chars) {
		int start = -1;
		char c;
		do {
			start ++;
			c = str.charAt(start);
		} while (chars.indexOf(c) > -1);
				
		int end = str.length();
		do {
			end --;
			c = str.charAt(end);
		} while (chars.indexOf(c) > -1);
		return str.substring(start, end + 1);
	}

	public int parseInt(String val) {
		try {
			return Integer.parseInt(val);
		} catch (Exception e) {
			return -1;
		}
		
	}

	public static List stringToList(String s) {
		return stringToList(s,",");
	}
	public static List stringToList(String s,String delim) {
		List l = new LinkedList();
		StringTokenizer st = new StringTokenizer(s,delim);
		while (st.hasMoreTokens())
			l.add(st.nextToken());
		return l;
	}

	public static String listToString(List l) {
		return listToString(l,",");
	}
	public static String listToString(List l, String delim) {
		StringBuffer sb = new StringBuffer();
		if (l!=null) {
			for (Iterator i=l.iterator();i.hasNext();)
				sb.append((String)i.next()).append(delim);
			if (sb.length()>0) sb.setLength(sb.length()-1);
		}
		return sb.toString();
	}

	
	public static String HTMLtoUTF(String value) {
		StringBuffer sb = new StringBuffer(value);
		int p = -1;
		while ((p=sb.indexOf("&#"))!=-1) {
			String code = sb.substring(p+2,p+5);
			char c = (char)Integer.parseInt(code);
			sb.replace(p,p+6,""+c); 
		}
		return sb.toString();

	}




	final static String[] HTML_Entities =  
	 {	"quot","apos","amp","lt","gt","nbsp","iexcl",
		"curren","cent","pound","yen","brvbar","sect",
		"uml","copy","ordf","laquo","not","shy","reg",
		"trade","macr","deg","plusmn","sup2","sup3",
		"acute","micro","para","middot","cedil","sup1",
		"ordm","raquo","frac14","frac12","frac34",
		"iquest","times","divide","Agrave","Aacute",
		"Acirc","Atilde","Auml","Aring","AElig","Ccedil",
		"Egrave","Eacute","Ecirc","Euml","Igrave",
		"Iacute","Icirc","Iuml","ETH","Ntilde","Ograve",
		"Oacute","Ocirc","Otilde","Ouml","Oslash",
		"Ugrave","Uacute","Ucirc","Uuml","Yacute","THORN",
		"szlig","agrave","aacute","acirc","atilde","auml",
		"aring","aelig","ccedil","egrave","eacute","ecirc",
		"euml","igrave","iacute","icirc","iuml","eth",
		"ntilde","ograve","oacute","ocirc","otilde","ouml",
		"oslash","ugrave","uacute","ucirc","uuml","yacute",
		"thorn","yuml","OElig","oelig","Scaron","scaron",
		"Yuml","circ","tilde","ensp","emsp","thinsp",
		"zwnj","zwj","lrm","rlm","ndash","mdash","lsquo",
		"rsquo","sbquo","ldquo","rdquo","bdquo","dagger",
		"Dagger","hellip","permil","lsaquo","rsaquo","euro"	 };
	private static String[][] HEMap = null;
	private static int HEMap_minlen = 1000;
	static {
		HashMap hm = new HashMap();
		String entity; Integer entlen; int maxlen=0;
		for (int i=0;i<HTML_Entities.length;i++) {
			entity = HTML_Entities[i];
			entlen = new Integer(entity.length());
			ArrayList l = (ArrayList)hm.get(entlen);
			if (l==null) {
				l=new ArrayList();
				hm.put(entlen,l);
			}
			l.add(entity);
			if (maxlen<entlen.intValue()) maxlen=entlen.intValue();
			if (HEMap_minlen>entlen.intValue()) HEMap_minlen=entlen.intValue();
		}
		HEMap = new String[maxlen+1][];
		for (Iterator i=hm.keySet().iterator();i.hasNext();) {
			Integer len = (Integer)i.next();
			ArrayList entlist = (ArrayList) hm.get(len);
			String[] ents = new String[entlist.size()];
			HEMap[len.intValue()] = ents;
			for (int j=0;j<ents.length;j++) {
				ents[j]=(String)entlist.get(j);
			}
		}
	}
	public static String ampFix(String src) {
		int srclen=src.length(); int lastSrcIndex=srclen-1;int lastPossibleSrcIndex=srclen-4;
		StringBuffer dst = new StringBuffer();
		int lastCutIndex = 0; final int cdata_len=9;
		boolean skipCData = false;
		boolean skipScript = false;
		for (int i=0;i<srclen;i++) {
			if (skipCData) { //close cdata check ]]>
				if (skipCData = !(i+3<srclen && 
							src.charAt(i)==']' && 
							src.charAt(i+1)==']' && 
							src.charAt(i+2)=='>')) continue;
				
			}
			if (skipScript) { //close check </script>
				if (skipScript = !(i+9<srclen && 
							src.substring(i,i+9).toLowerCase().equals("</script>"))) continue;
				
			}
			//cdata check <![CDATA[
			skipCData = i+9<srclen && 
						src.charAt(i)=='<' && 
						src.charAt(i+8)=='[' && 
						src.charAt(i+1)=='!' && 
						src.charAt(i+2)=='[' && 
						src.charAt(i+3)=='C' && 
						src.charAt(i+4)=='D' && 
						src.charAt(i+5)=='A' && 
						src.charAt(i+6)=='T' && 
						src.charAt(i+7)=='A';
			if (skipCData) {
				i+=9;
				continue; 
			}
			//script tag check <script
			skipScript = i+7<srclen && src.substring(i,i+7).toLowerCase().equals("<script");
			if (skipScript) {
				i+=6;
				continue; 
			}
			if (src.charAt(i)!='&') continue;
			if (i<=lastPossibleSrcIndex) {
				//check for entity code
				if (src.charAt(i+1)=='#') {
					boolean validCode = false;
					int si = i+2;
					//find numbers up to ; - ; must appear after 2,3 or 4 characters 
					if (src.charAt(si)>='0' && src.charAt(si)<='9') { //first ok ? must be a number
						int ei = si+4;
						si++;
						for (int j=si;j<ei;j++) { //other can be either number or ; (up to 4 characters)
							if (src.charAt(j)==';') {
								validCode=true;
								break;
							} else if (src.charAt(j)<'0' || src.charAt(j)>'9') break;
						}
					}
					if (!validCode) {
						//encode &
						dst.append(src.substring(lastCutIndex,i)).append("&amp;");
						lastCutIndex=i+1;
					}
				} else {
					//check in entity list
					boolean hit = false;
					for (int j=HEMap_minlen;j<HEMap.length;j++) {
						int scp = i+j+1;
						if (HEMap[j]!=null && scp<srclen && src.charAt(scp)==';') {
							int si = i+1;
							String[] entlist = HEMap[j]; 
							hit = false;
							for (int k=0;!hit && k<entlist.length;k++) {
								hit=true;
								for (int l=0;hit && l<j;l++) { // j==entity length
									hit = src.charAt(si+l)==entlist[k].charAt(l); 
								}
							}
							if (hit) break; //valid entity found, no encode
						}
					}
					if (!hit) {
						//encode &
						dst.append(src.substring(lastCutIndex,i)).append("&amp;");
						lastCutIndex=i+1;
					}
				}
			} else {
				//encode &
				dst.append(src.substring(lastCutIndex,i)).append("&amp;");
				lastCutIndex=i+1;
			}
		}
		if (lastCutIndex<srclen) dst.append(src.substring(lastCutIndex,srclen));
		return dst.toString();
		
	}



	static Random rnd = new Random(System.currentTimeMillis());
	public static String generatePassword(int no_chars) {
		String chars = "ABCDEFGHIJKLMNOPQRSTUWXYZabcdefghijklmnopqrstuwxyz0123456789";
		char[] pwd = new char[no_chars];
		for (int i=0;i<pwd.length;i++)
			pwd[i]=chars.charAt(rnd.nextInt(chars.length()));
		return new String(pwd);
	}














	public static void main(String[] args) {
		System.out.println(ampFix("mexo & pero <![CDATA[ hello & goodbye ]]> & mexo"));
	}




}
